/*
 * Copyright (c) 2007-2008, Thomas Krause (tho.krause@gmail.com)
 * 
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 * 
 *     * Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *     * Neither the name of gidoo nor the names of its contributors may be used to
 *       endorse or promote products derived from this software without specific
 *       prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
package org.gidoo.cms.publisher;

import org.gidoo.cms.login.GidooCMSSession;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.jcr.Node;
import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.Session;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.markup.html.AjaxLink;
import org.apache.wicket.markup.html.list.ListItem;
import org.apache.wicket.authorization.strategies.role.annotations.AuthorizeInstantiation;
import org.apache.wicket.behavior.HeaderContributor;
import org.apache.wicket.extensions.ajax.markup.html.modal.ModalWindow;
import org.apache.wicket.markup.html.WebPage;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.link.Link;
import org.apache.wicket.markup.html.list.ListView;
import org.apache.wicket.markup.html.panel.FeedbackPanel;
import org.apache.wicket.model.Model;
import org.apache.wicket.model.ResourceModel;
import org.gidoo.cms.GidooCMS;
import org.gidoo.cms.IPlugin;
import org.gidoo.cms.RedirectPage;
import org.gidoo.cms.admin.AdminPage;
import org.gidoo.cms.editor.edit.PreviewPage;
import org.gidoo.cms.helper.AdminNavPanel;
import org.gidoo.cms.helper.ResourceDownloadLink;
import org.gidoo.cms.helper.Tools;
import org.gidoo.cms.jcr.JcrHelper;
import org.gidoo.cms.view.XSLTTransformFailedException;
import org.gidoo.wickettools.XHTMLDiff;
import org.gidoo.wickettools.MessageBox;

/**
 *
 * @author thomas
 */
@AuthorizeInstantiation(GidooCMSSession.ROLE_PUBLISHER)
public class PublisherPage extends WebPage
{

  FeedbackPanel feedback;
  ListView lsvPublishPages;
  ListView lsvPublishResources;
  List<String> pathsForPagesToPublish;
  List<String> pathsForResourcesToPublish;
  GidooCMSSession theSession;
  ModalWindow window;
  
  ListView lsvPlugins;

  /** Creates a new instance of PublisherPage */
  public PublisherPage()
  {
    try
    {
      add(HeaderContributor.forCss(AdminPage.CSS_ADMIN));

      theSession = (GidooCMSSession) this.getSession();

      feedback = new FeedbackPanel("feedbackPanel");
      feedback.setOutputMarkupId(true);
      add(feedback);

      window = new ModalWindow("window");
      add(window);

      add(new Label("lblTitle", new ResourceModel("publisherPage")));
      add(new Link("lnkLogout")
      {

        public void onClick()
        {
          theSession.signOut();
          this.setResponsePage(RedirectPage.class);
        }
      });

      add(new AdminNavPanel("panelAdminNav", PublisherPage.class));
      
      add(new Link("lnkRefresh")
      {

        @Override
        public void onClick()
        {
          setResponsePage(PublisherPage.class);
        }
      });


      Node root = theSession.getJcrRootNode();
      NodeIterator itSections =
        root.getNode("users").getNode(theSession.getUsername()).getNode("publish").getNodes();
      List<String> sectionsOfUser = new LinkedList<String>();
      while(itSections.hasNext())
      {
        sectionsOfUser.add(itSections.nextNode().getName());
      }

      pathsForPagesToPublish = new ArrayList<String>();

      pathsForResourcesToPublish = new ArrayList<String>();

      NodeIterator itDrafts = root.getNode("drafts").getNodes();

      while(itDrafts.hasNext())
      {
        Node n = itDrafts.nextNode();
        try
        {
          boolean isResource = "cms:resource".equals(n.getPrimaryNodeType().getName());
          // find section for this node
          
          String path = Tools.draftName2Path(n.getName());
          String pathForOrig = path;
          if(isResource)
          {
            pathForOrig = pathForOrig.substring(0, pathForOrig.lastIndexOf("/"));
          }
          
          Node nOrig = root.getNode(pathForOrig);
          Node s = JcrHelper.getSectionOfNode(root, nOrig);

          if(s != null && sectionsOfUser.contains(s.getName()))
          {
            String type = n.getPrimaryNodeType().getName();
            if(isResource)
            {
              pathsForResourcesToPublish.add(path);
            }
            else if(type.equals("cms:page") || type.equals("cms:section"))
            {
              pathsForPagesToPublish.add(path);
            }
          }
        }
        catch(RepositoryException ex)
        {
          Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
          error(getString("jcrerror"));
        }
      }

      lsvPublishPages = new ListView("lsvPublishPages", pathsForPagesToPublish)
      {

        @Override
        protected void populateItem(ListItem item)
        {
          createPageEntries(item);
        }
      };

      add(lsvPublishPages);

      lsvPublishResources = new ListView("lsvPublishResources", pathsForResourcesToPublish)
      {

        @Override
        protected void populateItem(ListItem item)
        {
          createResourceEntries(item);
        }
      };

      add(lsvPublishResources);

      Label lblNoneResources = new Label("lblNoneResources", new Model(getString("none")));
      lblNoneResources.setVisible(pathsForResourcesToPublish.isEmpty());
      add(lblNoneResources);

      Label lblNonePages = new Label("lblNonePages", new Model(getString("none")));
      lblNonePages.setVisible(pathsForPagesToPublish.isEmpty());
      add(lblNonePages);
    }
    catch(RepositoryException ex)
    {
      Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
      error(getString("jcrerror"));
    }

    lsvPlugins = new ListView("lsvPlugins", ((GidooCMS) getApplication()).getPublishPlugins()) 
    {

      @Override
      protected void populateItem(ListItem item)
      {
        IPlugin p = (IPlugin) lsvPlugins.getList().get(item.getIndex());
        item.add(new Label("lblHeading", new Model(p.getPluginName())));
        item.add(p.getPluginPanel("panelPlugin"));
      }
    };
    add(lsvPlugins);
    
  }

  private void createPageEntries(ListItem item)
  {
    try
    {
      String path = pathsForPagesToPublish.get(item.getIndex());

      final Node root = ((GidooCMSSession) getSession()).getJcrRootNode();
      final Node page = root.getNode("drafts").getNode(Tools.path2DraftName(path));
      Node nOrig = root.getNode(path);
      final Node section = JcrHelper.getSectionOfNode(root, nOrig);


      item.add(new Label("lblPath", new Model(path.substring("sections/".length()))));
      item.add(new Label("lblSection", new Model(section.getName())));
      item.add(new Label("lblEditor", 
        new Model(page.hasProperty("cms:editor") ? 
          page.getProperty("cms:editor").getString() : "" )));

      Calendar c = page.getProperty("jcr:created").getDate();
      DateFormat formatter = DateFormat.getDateTimeInstance();
      Date d = new Date(c.getTimeInMillis());
      item.add(new Label("lblTime", new Model(formatter.format(d))));


      item.add(new AjaxLink("lnkPreview")
      {
        @Override
        public void onClick(AjaxRequestTarget target)
        {
          if(page != null)
          {
            try
            {
              window.setPageCreator(new ModalWindow.PageCreator()
              {

                public org.apache.wicket.Page createPage()
                {
                  try
                  {
                    return new PreviewPage(page.getProperty("cms:xml").getString());
                  }
                  catch(XSLTTransformFailedException ex)
                  {
                    Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
                    return null;
                  }
                  catch(RepositoryException ex)
                  {
                    Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
                    return null;
                  }
                }
              });
              window.setTitle(getString("preview") + ": " + page.getProperty("cms:title").getString());
              window.show(target);
            }
            catch(RepositoryException ex)
            {
              Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
              error(getString("jcrerror"));
            }
          }
          target.addComponent(feedback);
        }
      });

      item.add(new Link("lnkAccept")
      {

        @Override
        public void onClick()
        {
          publishPage(root, page);
        }
      });

      item.add(new Link("lnkReject")
      {

        @Override
        public void onClick()
        {
          rejectPage(page);
        }
      });

      item.add(new Link("lnkDiff")
      {

        @Override
        public void onClick()
        {
          try
          {
            showDiff(page);
          }
          catch(RepositoryException ex)
          {
            Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
            error(getString("jcrerror"));
          }
        }
      });
    }
    catch(RepositoryException ex)
    {
      Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
    }
  }

  private void createResourceEntries(ListItem item)
  {
    try
    {

      String path = pathsForResourcesToPublish.get(item.getIndex());
      final Session session = ((GidooCMSSession) getSession()).getJcrSession();
      Node root = ((GidooCMSSession) getSession()).getJcrRootNode();
      Node drafts = root.getNode("drafts");
      final Node res = drafts.getNode(Tools.path2DraftName(path));

      item.add(new Label("lblPath", new Model(path)));
      item.add(new Label("lblType", new Model(res.getProperty("jcr:mimeType").getString())));
      item.add(new Label("lblEditor", new Model(res.getProperty("cms:editor").getString())));

      ResourceDownloadLink lnkShow = new ResourceDownloadLink("lnkShow", res);
      item.add(lnkShow);

      item.add(new Link("lnkAccept")
      {

        @Override
        public void onClick()
        {
          publishResource(session, res);
        }
      });

      item.add(new Link("lnkReject")
      {

        @Override
        public void onClick()
        {
          rejectResource(res);
        }
      });
    }
    catch(RepositoryException ex)
    {
      Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
      error(getString("jcrerror"));
    }

  }

  private void publishPage(Node root, Node page)
  {
    try
    {
      String path = Tools.draftName2Path(page.getName());
      Node oldPublished = root.getNode(path);

      oldPublished.checkin();
      oldPublished.checkout();
      
      JcrHelper.copyPageProperties(page, oldPublished);

      oldPublished.setProperty("cms:publisher", ((GidooCMSSession) getSession()).getUsername());

      oldPublished.checkout();

      page.remove();

      root.save();
    }
    catch(RepositoryException ex)
    {
      Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
      error(getString("jcrerror"));
    }

    setResponsePage(PublisherPage.class);

  }

  private void publishResource(Session session, Node res)
  {
    try
    {
      GidooCMSSession gSession = (GidooCMSSession) getSession();
      String path = Tools.draftName2Path(res.getName());
      if(session.getRootNode().hasNode(path))
      {
        // ups, there is already a node with this path, delete the draft and
        // and inform the user
        warn(getString("resexists"));
        Node parent = res.getParent();
        res.remove();
        parent.save();
        return;
      }
      
      res.setProperty("cms:publisher", gSession.getUsername());
      res.setProperty("jcr:lastModified", Calendar.getInstance());
      
      session.move(res.getPath(), "/" + path);
      session.save();
    }
    catch(RepositoryException ex)
    {
      Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
      error(getString("jcrerror"));
    }

    setResponsePage(PublisherPage.class);
  }

  private void rejectPage(Node page)
  {
    try
    {
      Node parent = page.getParent();
      page.remove();
      parent.save();
    }
    catch(RepositoryException ex)
    {
      Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
      error(getString("jcrerror"));
    }

    setResponsePage(PublisherPage.class);
  }

  private void rejectResource(Node res)
  {
    try
    {
      Node parent = res.getParent();
      res.remove();
      parent.save();
    }
    catch(RepositoryException ex)
    {
      Logger.getLogger(PublisherPage.class.getName()).log(Level.SEVERE, null, ex);
      error(getString("jcrerror"));
    }

    setResponsePage(PublisherPage.class);
  }

  private void showDiff(Node newer) throws RepositoryException
  {
    Node rootNode = ((GidooCMSSession) getSession()).getJcrRootNode();
    String path = Tools.draftName2Path(newer.getName());
    Node old = rootNode.getNode(path);

    String diff = "";

    String titleOld = "";

    if(old == null || !old.hasProperty("cms:xml") || !old.hasProperty("cms:title"))
    {
      diff = XHTMLDiff.doDiff(getString("isnew"), newer.getProperty("cms:xml").getString());
    }
    else
    {
      diff = XHTMLDiff.doDiff(old.getProperty("cms:xml").getString(), newer.getProperty("cms:xml").getString());
      titleOld = old.getProperty("cms:title").getString();
    }

    diff =
      "<p>" + getString("oldtitle") + titleOld + "<br />" +
      getString("newtitle") + newer.getProperty("cms:title").getString() + "</p>" +
      "<table style=\"text-align:left;width:100%\"><tr style=\"text-align:center\">" +
      "<th>" + getString("old") + "</th><th>" + getString("new") + "</th>" +
      diff + "</table>";

    MessageBox msgbox = new MessageBox(getString("difftitle"), diff, true,
      MessageBox.MessageBoxButtons.OK, this, null);
    this.setResponsePage(msgbox);
  }
}
